在Unity中通过Camera实现类似地图拉拽缩放的功能

您所在的位置:网站首页 unity原神 背景 在Unity中通过Camera实现类似地图拉拽缩放的功能

在Unity中通过Camera实现类似地图拉拽缩放的功能

2023-11-04 19:55| 来源: 网络整理| 查看: 265

在最近的工作中任务是制作一个导览图系统,需要用到的一个需求就是可以拖动和放大地图,参考网上有很多UI描点大法的实现,我想尝试在摄像机上去实现。

0de55f6632c044e09d7b26dfbe60a071.gif

由于地图偏平面,我使用的是用2D模式下的摄像机,将摄像机的Projection(投影)属性设置成,Orthographic。

我首先想到的是通过直接改变摄像机Size实现缩放效果,通过 Input.GetAxis("Mouse ScrollWheel")获取鼠标滚轮的滚动量来改变Size的大小。

public Camera me_camera; // 参与变换的摄像机 public Vector2 range_size = new Vector2(1, 5); // 通过这个来控制Size缩放量 // 位置缩放 public void ScreenZoom() { float v = Input.GetAxis("Mouse ScrollWheel"); //获取鼠标滚轮方向 float old_size = me_camera.orthographicSize; // 计算摄像机原来的大小 // 计算移动后的大小 float size = Mathf.Clamp(old_size - v, range_size.x, range_size.y); me_camera.orthographicSize = size; }

 不过这个效果emmm怎么说吧,通过和别人的地图作比较发现,还是差一些意思。

a3feec8887cb40459dbfd0e5f64da75f.gif

 

最后还是找到一个贴子参考,看了他人的文章一下子悟了,参考文章如下。

根据观察,我发现一个规律,别人的缩放是在视点缩放,在摄像机size缩小时位置会无限接近视点位置,而且的移动的长度(相对摄像机到视点的位置)和摄像机size缩放的倍数是相同的,比如摄像机size比原来缩小了 1/2 摄像机就会向视点位置移动原位置的 1/2 。

5440329484214646b65975208e64f50c.png

假设摄像机原来大小是oldSize、位置是pos,缩小后的大小是size、位置是newPos, 视点使用鼠标位置为mousePos,所以得到如下公式:

gif.latex?%5Cfn_jvn%20%5Csmall%20%5Cfrac%7BoldSize-size%7D%7BoldSize%7D%20%3D%20%5Cfrac%7B%28mousePos%20-%20pos%29%20-%28mousePos%20-%20newPos%29%20%7D%7BmousePos%20-%20pos%7D

 其中因为视点到摄像机的位置是在坐标系上的,要排除掉原点到摄像机的距离,因为其他条件是已知的,将公式简化则可以得到 newPos。

gif.latex?%5Cfn_jvn%20%5Csmall%20newPos%20%3D%20pos%20-%20%28mousePos%20-%20pos%20%29%20*%20%5Cfrac%7BoldSize%20-%20size%7D%7BoldSize%20%7D

代码修改如下:

public Camera me_camera; // 参与变换的摄像机 public Vector2 range_size = new Vector2(1, 5); // 通过这个来控制Size缩放量 /// /// 根据视点缩放 /// public void AnchorZoom() { float v = Input.GetAxis("Mouse ScrollWheel"); // 计算摄像机原来的大小 float old_size = me_camera.orthographicSize; // 计算移动后的大小 float size = Mathf.Clamp(old_size - v, range_size.x, range_size.y); // 计算摄像机的缩放倍数 float pro = (old_size - size) / old_size; // 公式 Vector3 position = me_camera.transform.position + (me_camera.ScreenToWorldPoint(Input.mousePosition) - me_camera.transform.position) * pro; // 应用大小缩放 me_camera.orthographicSize = size; //位置缩放 me_camera.transform.position = new Vector3(position.x, position.y, me_camera.transform.position.z); }

其中,mousePosition 的值需要经过ScreenToWorldPoint( )  方法来转换成世界坐标去进行运算。

缩放搞定,然后是平移功能。摄像机平移功能比较简单,我直接采用的就是平移的方法。

// 参与变换的摄像机 public Camera me_camera; // 通过这个来控制画面缩放量 public Vector2 range_size = new Vector2(2, 5); public void ScreenMove() { // 鼠标 左键 float x = Input.GetAxis("Mouse X"); float y = Input.GetAxis("Mouse Y"); if (Input.GetMouseButton(0)) { // 相机位置的偏移量(Vector3类型,实现原理是:向量的加法) Vector3 moveDir = (x * -me_camera.transform.right) + (y * -me_camera.transform.up); // 使平移速度被size影响 size 越小移动越慢 float speed = (me_camera.orthographicSize / range_size.y) * 0.5f; moveDir = moveDir * speed; // 限制y轴的偏移量 me_camera.transform.position += moveDir; } }

      制作一个将摄像机的可移动画面划一个范围,超过范围慢慢会回来的效果。于是我将摄像机想象成一个矩形rect,在它的外面有一个更大的矩形rangeRect,然后你只需要判断rect是不是在rangeRect里面。在Unity中直接有Rect可以使用,只需要取分别的顶点在世界坐标里的位置就可以了,这样就可以算出宽和高。

49369aba0e5042409ee850bdd0dd21a9.png

 加上限制的平移代码如下:

// 参与变换的摄像机 public Camera me_camera; // 通过这个来控制画面缩放量 public Vector2 range_size = new Vector2(2, 5); // 限制运动范围矩形 Rect rect, rangeRect; void Start() { Camera.main.orthographicSize = range_size.y; // 在这里定义矩形限制的范围 var Start_leftTop = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0)); var Start_rightButtom = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0)); rangeRect = new Rect(Start_leftTop.x, Start_leftTop.y, Mathf.Abs(Start_rightButtom.x - Start_leftTop.x), Mathf.Abs(Start_rightButtom.y - Start_leftTop.y)); Camera.main.orthographicSize = range_size.x; } void Update() { ScreenMove(); } public void ScreenMove() { // 鼠标 左键 float x = Input.GetAxis("Mouse X"); float y = Input.GetAxis("Mouse Y"); if (Input.GetMouseButton(0)) { // 相机位置的偏移量(Vector3类型,实现原理是:向量的加法) Vector3 moveDir = (x * -me_camera.transform.right) + (y * -me_camera.transform.up); // 使平移速度被size影响 size 越小移动越慢 float speed = (me_camera.orthographicSize / range_size.y) * 0.5f; moveDir = moveDir * speed; // 限制y轴的偏移量 me_camera.transform.position += moveDir; } else //限制摄像机运动范围 { UpdateNowCameraRect(); //是否出顶 if (rect.y > rangeRect.y) { float d = rect.y - rangeRect.y; me_camera.transform.position -= new Vector3(0, d * 0.05f, 0); } // 底低于底部 if (rect.y - rect.height < rangeRect.y - rangeRect.height) { float d = (rangeRect.y - rangeRect.height) - (rect.y - rect.height); me_camera.transform.position -= new Vector3(0, -d * 0.05f, 0); } // 低于最左边 if (rect.x < rangeRect.x) { float d = rangeRect.x - rect.x; me_camera.transform.position -= new Vector3(-d * 0.05f, 0, 0); } // 高于最右边 if (rect.x + rect.width > rangeRect.x + rangeRect.width) { float d = (rect.x + rect.width) - (rangeRect.x + rangeRect.width); me_camera.transform.position -= new Vector3(d * 0.05f, 0, 0); } } } /// /// 更新当前摄像机的矩形 /// void UpdateNowCameraRect() { var Now_leftTop = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0)); var Now_rightButtom = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0)); rect.Set(Now_leftTop.x, Now_leftTop.y, Mathf.Abs(Now_rightButtom.x - Now_leftTop.x), Mathf.Abs(Now_rightButtom.y - Now_leftTop.y)); }

 平移和缩放都完成,最终效果如下。

e9ed2f065ce14fa58606490d15f13e7f.gif

 最后附上所有代码,里面有触控屏上的移动方法,直接下载用就行,因为太长我就不再这里展示啦。

https://download.csdn.net/download/HunagJingLiao/87267232

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3